/**
 * 
 */
package com.novartis.nims.trialsubmitter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;
import java.util.Set;

import com.novartis.nims.util.ResourceAccess;
import com.pixelmed.dicom.AttributeTag;

/**
 * @author snydejo1
 *
 */
public class ApplicationConfiguration {

	private static final String defaultPropFile = "nits.default.properties";
	private static final String propFile = "nits.properties";

	private Properties mProperties;
	
	private String mCROName;
	private String mVisitNameSeparator;
	private String mUnscheduledVisitIdentifier;
	private String mUnscheduledVisitNumber;
	private String mExtensionNumber;
	private AttributeTag mVisitKey;
	private AttributeTag mScanKey;
	private AttributeTag mFileKey;
	private AttributeTag mScanName;
	private boolean mAdvancedInterface;
	private boolean mDoSort;
	private boolean mDoClean;
	private boolean mDoODM;
	private boolean mVisitsOrganized;
	private boolean mScansOrganized;
	private boolean mAbortOnFileMissingVisitKey;
	private boolean mAbortOnFileMissingScanKey;
	private boolean mAbortOnFileMissingFileKey;
	private boolean mAbortOnFileMissingScanName;
	private boolean mAbortOnFileWithDuplicateFileKey;
	private boolean mAbortOnUnspecifiedFileError;
	private boolean mAllowBadPixelData;
	private int mFileMinimumKB;

	
	/**
	 * @throws IOException 
	 * 
	 */
	ApplicationConfiguration() throws ApplicationConfigurationException {

		//initialize default properties
		InputStream defaults = ResourceAccess.getResourceAsStream(ApplicationConfiguration.class, defaultPropFile);
		Properties defaultProps = new Properties();
		try {
			BufferedReader propReader = new BufferedReader(new InputStreamReader(defaults));
			try {
				defaultProps.load(propReader);
			} catch (IOException e) {
				throw new ApplicationConfigurationException("Couldn't load configuration. There was probably " +
						"a mistake during configuration or deployment", e);
			}
		} finally {
			try {
				defaults.close();
			} catch (IOException ignore) { }
		}
		
		
		InputStream is = ResourceAccess.getResourceAsStream(ApplicationConfiguration.class, propFile);
		if (is == null) {
			//TODO define new exception type to use
			throw new ApplicationConfigurationException("Configuration file not found. There was probably " +
					"a mistake during configuration or deployment");
		}
		
		try {
			BufferedReader propReader = new BufferedReader(new InputStreamReader(is));
			mProperties = new Properties(defaultProps);
			try {
				mProperties.load(propReader);
			} catch (IOException e) {
				throw new ApplicationConfigurationException("Couldn't load configuration. There was probably " +
					"a mistake during configuration or deployment", e);
			}

			String value = null;

			value = mProperties.getProperty("CROName");
			if (value.isEmpty()) {
				throw new ApplicationConfigurationException("Configuration error. Make sure setting for " +
					"CROName isn't empty.");
			} else {
				mCROName = value;
			}

			value = mProperties.getProperty("Extension");
			if (value.isEmpty()) {
				throw new ApplicationConfigurationException("Configuration error. Make sure setting for " +
					"Extension isn't empty.");
			} else {
				mExtensionNumber = value;
			}

			value = mProperties.getProperty("UnscheduledVisitIdentifier");
			if (value.isEmpty()) {
				throw new ApplicationConfigurationException("Configuration error. Make sure setting for " +
					"UnscheduledVisitIdentifier isn't empty.");
			} else {
				mUnscheduledVisitIdentifier = value;
			}

			value = mProperties.getProperty("UnscheduledVisitNumber");
			if (value.isEmpty()) {
				throw new ApplicationConfigurationException("Configuration error. Make sure setting for " +
					"UnscheduledVisitNumber isn't empty.");
			} else {
				mUnscheduledVisitNumber = value;
			}

			value = mProperties.getProperty("VisitNameSeparator");
			if (value.isEmpty()) {
				throw new ApplicationConfigurationException("Configuration error. Make sure setting for " +
					"VisitNameSeparator isn't empty.");
			} else {
				mVisitNameSeparator = value;
			}

			mVisitKey = tagFromString(mProperties.getProperty("VisitKey"));
			mScanKey = tagFromString(mProperties.getProperty("ScanKey"));
			mFileKey = tagFromString(mProperties.getProperty("FileKey"));
			mScanName = tagFromString(mProperties.getProperty("ScanName"));


			value = mProperties.getProperty("AllowBadPixelData");
			if (value.equalsIgnoreCase("yes")) mAllowBadPixelData = true;
			else if (value.equalsIgnoreCase("no")) mAllowBadPixelData = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"AllowBadPixelData to Yes or No.");
			
			value = mProperties.getProperty("AdvancedInterface");
			if (value.equalsIgnoreCase("yes")) mAdvancedInterface = true;
			else if (value.equalsIgnoreCase("no")) mAdvancedInterface = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"AdvancedInterface to Yes or No.");

			value = mProperties.getProperty("Sort");
			if (value.equalsIgnoreCase("yes")) mDoSort = true;
			else if (value.equalsIgnoreCase("no")) mDoSort = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"Sort to Yes or No.");

			value = mProperties.getProperty("Clean");
			if (value.equalsIgnoreCase("yes")) mDoClean = true;
			else if (value.equalsIgnoreCase("no")) mDoClean = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"Clean to Yes or No.");

			value = mProperties.getProperty("ODM");
			if (value.equalsIgnoreCase("yes")) mDoODM = true;
			else if (value.equalsIgnoreCase("no")) mDoODM = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"ODM to Yes or No.");

			value = mProperties.getProperty("VisitsOrganized");
			if (value.equalsIgnoreCase("yes")) mVisitsOrganized = true;
			else if (value.equalsIgnoreCase("no")) mVisitsOrganized = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"VisitsOrganized to Yes or No.");

			value = mProperties.getProperty("ScansOrganized");
			if (value.equalsIgnoreCase("yes")) mScansOrganized = true;
			else if (value.equalsIgnoreCase("no")) mScansOrganized = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"ScansOrganized to Yes or No.");

			value = mProperties.getProperty("AbortOnFileMissingVisitKey");
			if (value.equalsIgnoreCase("yes")) mAbortOnFileMissingVisitKey = true;
			else if (value.equalsIgnoreCase("no")) mAbortOnFileMissingVisitKey = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"AbortOnFileMissingVisitKey to Yes or No.");

			value = mProperties.getProperty("AbortOnFileMissingScanKey");
			if (value.equalsIgnoreCase("yes")) mAbortOnFileMissingScanKey = true;
			else if (value.equalsIgnoreCase("no")) mAbortOnFileMissingScanKey = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"AbortOnFileMissingScanKey to Yes or No.");

			value = mProperties.getProperty("AbortOnFileMissingFileKey");
			if (value.equalsIgnoreCase("yes")) mAbortOnFileMissingFileKey = true;
			else if (value.equalsIgnoreCase("no")) mAbortOnFileMissingFileKey = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"AbortOnFileMissingFileKey to Yes or No.");

			value = mProperties.getProperty("AbortOnFileMissingScanName");
			if (value.equalsIgnoreCase("yes")) mAbortOnFileMissingScanName = true;
			else if (value.equalsIgnoreCase("no")) mAbortOnFileMissingScanName = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"AbortOnFileMissingScanName to Yes or No.");

			value = mProperties.getProperty("AbortOnFileWithDuplicateFileKey");
			if (value.equalsIgnoreCase("yes")) mAbortOnFileWithDuplicateFileKey = true;
			else if (value.equalsIgnoreCase("no")) mAbortOnFileWithDuplicateFileKey = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"AbortOnFileWithDuplicateFileKey to Yes or No.");

			value = mProperties.getProperty("AbortOnUnspecifiedFileError");
			if (value.equalsIgnoreCase("yes")) mAbortOnUnspecifiedFileError = true;
			else if (value.equalsIgnoreCase("no")) mAbortOnUnspecifiedFileError = false;
			else throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"AbortOnUnspecifiedFileError to Yes or No.");


			value = mProperties.getProperty("FileMinimumKB");
			try {
				mFileMinimumKB = Integer.parseInt(value);
				if (mFileMinimumKB < 1) {
					throw new NumberFormatException();
				}
			} catch (NumberFormatException e) {
				throw new ApplicationConfigurationException("Configuration error. Try setting value for " +
				"FileMinimumKB to a positive integer value.");
			}
		} finally {
			try {
				is.close();
			} catch (IOException ignore) { }
		}
		
	}
	
	public String getExtensionNumber() {
		return mExtensionNumber;
	}
	
	public void setExtensionNumber(String extensionNumber) {
		mExtensionNumber = extensionNumber;
	}
	
	public boolean getAdvancedInterface() {
		return mAdvancedInterface;
	}
	
	public boolean getDoSort() {
		return mDoSort;
	}
	
	public boolean getDoClean() {
		return mDoClean;
	}
	
	public boolean getDoODM() {
		return mDoODM;
	}
	
	public boolean getVisitsOrganized() {
		return mVisitsOrganized;
	}
	
	public boolean getScansOrganized() {
		return mScansOrganized;
	}

	public boolean getAbortOnFileMissingVisitKey() {
		return mAbortOnFileMissingVisitKey;
	}

	public boolean getAbortOnFileMissingScanKey() {
		return mAbortOnFileMissingScanKey;
	}

	public boolean getAbortOnFileMissingFileKey() {
		return mAbortOnFileMissingFileKey;
	}

	public boolean getAbortOnFileMissingScanName() {
		return mAbortOnFileMissingScanName;
	}

	public boolean getAbortOnFileWithDuplicateFileKey() {
		return mAbortOnFileWithDuplicateFileKey;
	}

	public boolean getAbortOnUnspecifiedFileError() {
		return mAbortOnUnspecifiedFileError;
	}

	public boolean getAllowBadPixelData() {
		return mAllowBadPixelData;
	}

	public int getFileMinimumKB() {
		return mFileMinimumKB;
	}

	public String getCROName() {
		return mCROName;
	}

	public String getVisitNameSeparator() {
		return mVisitNameSeparator;
	}

	public String getUnscheduledVisitIdentifier() {
		return mUnscheduledVisitIdentifier;
	}

	public String getUnscheduledVisitNumber() {
		return mUnscheduledVisitNumber;
	}

	public AttributeTag getVisitKey() {
		return mVisitKey;
	}

	public AttributeTag getScanKey() {
		return mScanKey;
	}

	public AttributeTag getFileKey() {
		return mFileKey;
	}

	public AttributeTag getScanName() {
		return mScanName;
	}

	public String getProperty(String key) {
		return mProperties.getProperty(key);
	}

	public Object setProperty(String key, String value) {
		return mProperties.setProperty(key, value);
	}
	
	public Set<String> stringPropertyNames() {
		return mProperties.stringPropertyNames();
	}

	
	// string must take form "gggg,eeee", where g and e are hexadecimal digits
	private AttributeTag tagFromString(String string) throws ApplicationConfigurationException {
		
		// split image keys into dicom group and element components
		String[] tag = string.split(",");
		if (tag.length != 2) {
			throw new ApplicationConfigurationException("Configuration error. Make sure setting for " +
				"VisitKey follows the format gggg,eeee.");
		}
		

		// convert group and element strings to integers using base 16
		int group, element;
		try {
			group = Integer.parseInt(tag[0], 16);
			element = Integer.parseInt(tag[1], 16);
			return new AttributeTag(group, element);
		} catch (NumberFormatException e) {
			throw new ApplicationConfigurationException("Configuration error. Make sure setting for " +
				"VisitKey follows the format gggg,eeee.");
		}
	}
	
}
